import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 锁：
 * 公平：按照申请锁的顺序，采用队列的方式，先进先出。缺点吞吐量小
 * 非公平：先获得锁，如果没有获得锁，安装公平锁的方式进行。优点吞吐量大
 * ReentrantLock(false),默认为非公平锁
 * 可重入锁（又递归锁）：指的是同一线程在调用同步方法访问另一个同步方法，可以再重新获得该锁,不同线程不能多次获取锁
 * 自旋锁：线程多次循环，直到获取该锁
 */


public class LockDemos {

    public static void main(String[] args) {

        cq cq = new cq();

        new Thread(() -> {
            Thread.currentThread().setName("t1");
            cq.get();
        }).start();

        new Thread(() -> {
            Thread.currentThread().setName("t2");
            cq.get();
        }).start();

        new Thread(() -> {
            Thread.currentThread().setName("t3");
            cq.write();
        }).start();

        new Thread(() -> {
            Thread.currentThread().setName("t4");
            cq.write();
        }).start();
    }

}

/**
 * 可重入锁
 */
class cq {

    public synchronized void get() {
        System.out.println(Thread.currentThread().getName() + " get() sync 可重入锁演示 \n");
        set();
    }

    public synchronized void set() {
        System.out.println(Thread.currentThread().getName() + " set() sync 可重入锁演示" + "\n");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void write() {
        ReentrantLock lock = new ReentrantLock();
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " write() lock 可重入锁演示 \n");
            read();
        } finally {
            lock.unlock();
        }
    }

    public void read() {
        ReentrantLock lock = new ReentrantLock();
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " read() lock 可重入锁演示 \n");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        } finally {
            lock.unlock();
        }
    }
}

/**
 * 自旋锁演示
 * cas 方式 注意: ABA问题存在，加入版本号
 */
class zx {

    // 原子类对象
    AtomicReference<Thread> atomicReference = new AtomicReference();
    Thread thread = new Thread();

    public void lock() {
        do {
            System.out.println(Thread.currentThread().getName() + " cas方式实现自旋锁，获取锁");
        } while (!atomicReference.compareAndSet(null, thread));
    }

    public void unLock() {
        System.out.println(Thread.currentThread().getName() + " cas方式实现自旋锁，解锁");
        atomicReference.compareAndSet(thread, null);
    }

    public static void main(String[] args) {
        zx zx = new zx();
        for (int i = 0; i < 2; i++) {
            new Thread(new MyRunable("t" + i, zx)).start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            Thread.sleep(50000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class MyRunable implements Runnable {

    zx zx;
    String name;

    public MyRunable(String name, zx zx) {
        this.name = name;
        this.zx = zx;
    }

    @Override
    public void run() {
        Thread.currentThread().setName(name);
        zx.lock();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
        }
        zx.unLock();
    }
}

/**
 * CountdownLatch 演示
 * ：当前线程会被阻塞，直到触发了条件后，再执行，每次--
 * 如：教室学生走完了，再关灯
 */
class MyCountdownLatch {

    public static void main(String[] args) {

        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 1; i < 7; i++) {
            final int j = i;
            new Thread(() -> {
                Thread.currentThread().setName("t" + j);
                leaveRoom();
                countDownLatch.countDown();
            }).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("学生走完，关闭教室");
    }

    public static void leaveRoom() {
        System.out.println("学生" + Thread.currentThread().getName() + "离开了教室");
    }
}

/**
 * CyclicBarrier：所有线程会被阻塞，知道条件满足在一起执行，每次++
 */
class MyCyclicBarrier {

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(6, () ->
                System.out.println("学生都来了，开始上课"));
        for (int i = 1; i < 7; i++) {
            final int j = i;
            new Thread(() -> {
                Thread.currentThread().setName("t" + j);
                reRoom();
                try {
                    int await = cyclicBarrier.await();
                    System.out.println("第" + await + "个学生");
                } catch (InterruptedException e) {

                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

    public static void reRoom() {
        System.out.println("学生" + Thread.currentThread().getName() + "来到了教室");
    }
}

/**
 * Semaphore ：信号量，协调各个线程，保证资源的合理运用
 * 例：6个学生抢3个座位
 */
class MySemaphore {
    // 3个座位
    static  Semaphore location = new Semaphore(3);

    public static void main(String[] args) {


        for (int i = 1; i < 7; i++) {
            final int j = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Thread.currentThread().setName("学生" + j);
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    qzw();
                }
            }).start();
        }
    }
    public static void qzw(){
        try {
            location.acquire(); //获取一个令牌，在获取到令牌、或者被其他线程调用中断之前线程一直处于阻塞状态。
            System.out.println(Thread.currentThread().getName() + "抢到座位");
            Thread.sleep(1000);
            location.release();//释放一个令牌，唤醒一个获取令牌不成功的阻塞线程。
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}